package cn.pink.common.support.tools.node;

import cn.hutool.core.util.RandomUtil;
import cn.pink.common.support.tools.cache.CacheTool;
import cn.pink.common.support.tools.vertx.VertxTool;
import cn.pink.core.NodeInfo;
import cn.pink.core.NodeType;
import cn.pink.core.config.CacheConfig;
import cn.pink.core.config.DeployConfig;
import cn.pink.core.config.IronConfig;
import cn.pink.core.support.Log;
import cn.pink.core.support.SysException;
import com.hazelcast.cluster.Cluster;
import com.hazelcast.cluster.MembershipEvent;
import com.hazelcast.cluster.MembershipListener;
import com.hazelcast.core.Hazelcast;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 节点插件
 * @Author: pink
 * @Date: 2022/6/15 12:14
 */
public class NodeTool {
    private static NodeTool nodeTool = new NodeTool();

    private NodeTool() {

    }

    public static NodeTool getInstance() {
        return nodeTool;
    }

    private Vertx vertx;

    public void init(Vertx vertx) {
        this.vertx = vertx;
        registerClusterListener();
    }

    /**
     * 获取集群管理器
     */
    public Cluster getCluster() {
        return isSimpleDeploy() ? null : Hazelcast.getHazelcastInstanceByName(IronConfig.COMMON_GAME).getCluster();
    }

    /**
     * 注册集群监听
     */
    public void registerClusterListener() {
        Cluster cluster = NodeTool.getInstance().getCluster();

        if(cluster == null) {
            return;
        }

        cluster.addMembershipListener(new MembershipListener() {
            @Override
            public void memberAdded(MembershipEvent membershipEvent) {

            }

            @Override
            public void memberRemoved(MembershipEvent membershipEvent) {
                Log.system.info("cluster listener member remove: member={}", membershipEvent.getMember());

                unregisterNode(membershipEvent.getMember().getUuid().toString());
            }
        });
    }

    /**
     * 注册节点
     */
    public Future<Void> registerNode(String nodeId, String portId, NodeType nodeType) {
        Promise<Void> promise = Promise.promise();

        VertxTool.getInstance().tryLock("reg").onSuccess(lock -> {
            NodeInfo nodeInfo = new NodeInfo(nodeId, portId, nodeType);

            String localNodeId = "default";

            if(getCluster() != null) {
                localNodeId = getCluster().getLocalMember().getUuid().toString();
            }

            List<NodeInfo> nodeList = CacheTool.getInstance().getCache(CacheConfig.NODE).get(localNodeId);
            if(nodeList == null) {
                nodeList = new ArrayList<>();
            }

            nodeList.add(nodeInfo);
            CacheTool.getInstance().getCache(CacheConfig.NODE).put(localNodeId, nodeList);

            lock.release();

            promise.complete();
        });

        return promise.future();
    }

    /**
     * 取消注册节点
     * @param id uuid
     */
    public void unregisterNode(String id) {
        CacheTool.getInstance().getCache(CacheConfig.NODE).remove(id);
    }

    /**
     * 获取所有节点信息
     */
    public List<NodeInfo> getAllNode() {
        List<NodeInfo> nodeList = new ArrayList<>();

        CacheTool.getInstance().getCache(CacheConfig.NODE).keys().forEach(k -> {
            List<NodeInfo> nodeInfos = CacheTool.getInstance().getCache(CacheConfig.NODE).get(k);
            if(nodeInfos != null) {
                nodeList.addAll(nodeInfos);
            }
        });

        return nodeList;
    }

    /**
     * 获取所有节点信息
     * @param id uuid
     */
    public List<NodeInfo> getAllNode(String id) {
        return CacheTool.getInstance().getCache(CacheConfig.NODE).get(id);
    }

    /**
     * 中心服唯一 直接获取
     */
    @SuppressWarnings("unchecked")
    public NodeInfo getCenterNode() {
        for(Object o : CacheTool.getInstance().getCache(CacheConfig.NODE).values()) {
            List<NodeInfo> nodeList = (List<NodeInfo>) o;
            Optional<NodeInfo> nodeInfo = nodeList.stream().filter(node -> node.getNodeType() == NodeType.CENTER).findFirst();
            if(nodeInfo.isPresent()) {
                return nodeInfo.get();
            }
        }

        return null;
    }

    /**
     * 根据节点类型随机获取一个可用节点
     * 集群环境下优先获取本地节点, 分布式环境随机
     */
    public NodeInfo getRandomNode(NodeType type) {
        if(type == NodeType.CENTER) {
            return getCenterNode();
        }

        List<NodeInfo> list = new ArrayList<>();

        if(isClusterDeploy()) {
            List<NodeInfo> nodeList = CacheTool.getInstance().getCache(CacheConfig.NODE).get(getCluster().getLocalMember().getUuid().toString());
            List<NodeInfo> nodeInfos = nodeList.stream().filter(node -> node.getNodeType() == type).collect(Collectors.toList());

            if(nodeInfos.isEmpty()) {
                nodeInfos.addAll(getNodeByType(type));
            }

            list.addAll(nodeInfos);
        }
        else {
            list.addAll(getNodeByType(type));
        }

        if(list.isEmpty()) {
            Log.error.error("cant empty!!!, nodeType={}", type);
            throw new SysException("随机不到节点!!!");
        }

        return RandomUtil.randomEle(list);
    }

    /**
     * 根据类型获取所有节点
     */
    @SuppressWarnings("unchecked")
    public List<NodeInfo> getNodeByType(NodeType type) {
        List<NodeInfo> nodeInfoList = new ArrayList<>();

        for(Object o : CacheTool.getInstance().getCache(CacheConfig.NODE).values()) {
            List<NodeInfo> nodeList = (List<NodeInfo>) o;
            List<NodeInfo> nodeInfos = nodeList.stream().filter(node -> node.getNodeType() == type).collect(Collectors.toList());
            nodeInfoList.addAll(nodeInfos);
        }

        return nodeInfoList;
    }

    /**
     * 是否集群部署
     */
    public boolean isClusterDeploy() {
        return IronConfig.COMMON_DEPLOY == DeployConfig.CLUSTER;
    }

    /**
     * 是否分布式部署
     */
    public boolean isDistributedDeploy() {
        return IronConfig.COMMON_DEPLOY == DeployConfig.DISTRIBUTED;
    }

    /**
     * 是否单机部署
     */
    public boolean isSimpleDeploy() {
        return IronConfig.COMMON_DEPLOY == DeployConfig.SIMPLE;
    }
}
